/*
 * @(#)XfvhdlOutputFlcFile.java        3.0                    2004/09/14
 *
 * This file is part of Xfuzzy 3.0, a design environment for fuzzy logic
 * based systems.
 *
 * (c) 2000 IMSE-CNM. The authors may be contacted by the email address:
 *                    xfuzzy-team@imse.cnm.es
 *
 * Xfuzzy is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Xfuzzy is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

package xfuzzy.xfvhdl;

import xfuzzy.lang.*;

/**
* Clase que genera el fichero con el c�digo VHDL del controlador difuso.
* @author Jos� Mar�a �vila Maireles, <b>e-mail</b>: josavimai@alum.us.es
* @version 3.0
*/
public class XfvhdlOutputFlcFile {

   //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
   //			  M�TO_DOS P�BLICOS DE LA CLASE				        
   //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//

   /**
   * M�todo que crea la cadena que ser� escrita en el fichero de salida.
   * @return Devuelve la cadena que ser� escrita en el fichero de salida.
   */
   public String createOutputFlcSource(
      Specification spec,
      XfvhdlIDefuzzification defuzzification) {

      XfvhdlHeadFile head =
         new XfvhdlHeadFile(
            XfvhdlProperties.fileDir,
            XfvhdlProperties.outputFile,
            XfvhdlProperties.ficheroXFL);

      String code = head.getHead();

      code
         += "\n--***********************************************************************--\n"
         + "--                                                                       --\n"
         + "--   DESCRIPTION: This file contains the VHDL description for the        --\n"
         + "--                fuzzy controller.                                      --\n"
         + "--                                                                       --\n"
         + "---------------------------------------------------------------------------\n"
         + "--                                                                       --\n"
         + "--   AUTHOR:      Jose Maria Avila Maireles                              --\n"
         + "--                                                                       --\n"
         + "--   VERSION:     Xfvhdl  ver0.1                            April 2004   --\n"
         + "--                                                                       --\n"
         + "--***********************************************************************--\n"
         + "\n"
         + "\n"
         + "library IEEE;\n"
         + "use IEEE.std_logic_1164.all;\n"
         + "\n"
         + "use WORK.Constants.all;\n"
         + "use WORK.Entities.all;\n"
         + "\n"
         + "\n"
         + "---------------------------------------------------------------------------\n"
         + "--                           Entity description                          --\n"
         + "---------------------------------------------------------------------------\n"
         + "\n"
         + "entity FLC is\n"
         + "\n"
         + "\tport(\tclk\t\t: in std_logic;\t\t\t\t\t-- Clock signal.\n"
         + "\t\treset\t\t: in std_logic;\t\t\t\t\t-- Reset signal.\n";

      for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
         code += "\t\tin"
            + i
            + "\t\t: in std_logic_vector(N downto 1);"
            + "\t\t-- Input "
            + i
            + " signal.\n";
      }

      code += "\t\toutput\t: out std_logic_vector(N downto 1);"
         + "\t\t-- Output signal.\n"
         + "\t\tvalid_out\t: out std_logic;"
         + "\t\t\t\t\t-- Valid output signal.\n"
         + "\t\tvalid_in\t: out std_logic);"
         + "\t\t\t\t\t-- Valid input signal.\n"
         + "\n"
         + "end FLC;\n"
         + "\n"
         + "\n"
         + "---------------------------------------------------------------------------\n"
         + "--                       Architecture description                        --\n"
         + "---------------------------------------------------------------------------\n"
         + "\n"
         + "architecture FPGA of FLC is\n\n";

      if (XfvhdlProperties.calcArithmetic == false)
         code += GenerateSignalWithoutCalcAritmetic();
      else
         code += GenerateSignalWithCalcAritmetic();

      if (XfvhdlProperties.simulation == true) {
         if (XfvhdlProperties.calcArithmetic == false) {
            code += "\n"
               + "\tfor all: Control1_nc\t\tuse entity "
               + "WORK.Control1_nc(FPGA);\n";

            for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
               code += "\tfor all: AntecedentMem_"
                  + i
                  + "\tuse entity WORK.AntecedentMem_"
                  + i
                  + "(FPGA);\n";
            }
         } else {
            code += "\n"
               + "\tfor all: Control2_nc\t\t"
               + "use entity WORK.Control2_nc(FPGA);\n"
               + "\tfor all: Arithmetic\t\t"
               + "use entity WORK.Arithmetic(FPGA);\n"
               + "\tfor all: ArithCalcMem\t\t"
               + "use entity WORK.ArithCalcMem(FPGA);\n";
         }

         code += "\tfor all: RulesMem\t\t\t"
            + "use entity WORK.RulesMem(FPGA);\n"
            + "\tfor all: MF_Grade\t\t\t"
            + "use entity WORK.MF_Grade(FPGA);\n"
            + "\tfor all: Minimum\t\t\t"
            + "use entity WORK.Minimum(FPGA);\n";

         //	A�ado a code el c�digo que depende del m�todo de 
         // defuzzificaci�n:	
         String tmp = new String();
         tmp = defuzzification.generateOutputFlcUse();
         code = code + tmp;

         code += "\tfor all: Division\t\t\t"
            + "use entity WORK.Division(FPGA);\n";
      }

      code += "\nbegin\n\n";

      if (XfvhdlProperties.calcArithmetic == false) {
         code += "\tvalid_in <= clear_almacen;\n"
            + "\n"
            + "\tControl: Control1_nc\n"
            + "\t\tport map(\tclk\t\t\t=> clk,\n"
            + "\t\t\t\treset\t\t\t=> reset,\n"
            + "\t\t\t\tme1\t\t\t=> me1,\n"
            + "\t\t\t\tme2\t\t\t=> me2,\n"
            + "\t\t\t\tpipeline\t\t=> pipeline,\n"
            + "\t\t\t\tclear_almacen\t=> clear_almacen,\n"
            + "\t\t\t\tacumulacion\t\t=> acumulacion,\n"
            + "\t\t\t\tdir2\t\t\t=> dir2);\n";

         // Lo siguiente a escribir en el fichero depende de la memoria
         // donde se valla a implementar el sistema.
         if (XfvhdlProperties.memoryType.equals((String) "ROM")
            || XfvhdlProperties.memoryType.equals(
               (String) "Combinational Logic")) {
            for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
               code += "\n\tMemAnt"
                  + i
                  + ": AntecedentMem_"
                  + i
                  + "\n"
                  + "\t\tport map(\taddr\t\t\t=> in"
                  + i
                  + ",\n"
                  + "\t\t\t\tme\t\t\t=> me1,\n"
                  + "\t\t\t\tdo\t\t\t=> do"
                  + i
                  + ");\n";
            }
         } else {
            for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
               code += "\n\tMemAnt"
                  + i
                  + ": Antecedent_RAM\n"
                  + "\t\tport map(\tclk\t\t\t=> clk,\n"
                  + "\t\t\t\twe\t\t\t=> me1,\n"
                  + "\t\t\t\taddr\t\t\t=> in"
                  + i
                  + ",\n"
                  + "\t\t\t\tdi\t\t\t=> do"
                  + i
                  + ",\n"
                  + "\t\t\t\tdo\t\t\t=> do"
                  + i
                  + ");\n";
            }
         }

         for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
            code += "\n\tMF_grad"
               + i
               + ": MF_Grade\n"
               + "\t\tport map(\tgrado1\t\t=> do"
               + i
               + "((2 * grad) downto (grad + 1)),\n"
               + "\t\t\t\tgrado2\t\t=> do"
               + i
               + "(grad downto 1),\n"
               + "\t\t\t\tnumfp\t\t\t=> do"
               + i
               + "(M downto ((2 * grad) + 1)),\n"
               + "\t\t\t\tentr_conta\t\t=> dir2("
               + i
               + "),\n"
               + "\t\t\t\tetiqueta\t\t=> addr_reg(((entradas - "
               + (i - 1)
               + ") * bits_etiq) downto \n\t\t\t\t\t\t\t "
               + "  (((entradas - "
               + i
               + ") * bits_etiq) + 1)),\n"
               + "\t\t\t\tgrado\t\t\t=> grados(("
               + i
               + " * grad) downto \n\t\t\t\t\t\t\t   (("
               + (i - 1)
               + " * grad) + 1)));\n";
         }
      } else {
         code += "\tvalid_in <= pipeline;\n"
            + "\n"
            + "\tControl: Control2_nc\n"
            + "\t\tport map(\tclk\t\t\t=> clk,\n"
            + "\t\t\t\treset\t\t\t=> reset,\n"
            + "\t\t\t\tme1\t\t\t=> me1,\n"
            + "\t\t\t\tme2\t\t\t=> me2,\n"
            + "\t\t\t\tpipeline\t\t=> pipeline,\n"
            + "\t\t\t\tclear_almacen\t=> clear_almacen,\n"
            + "\t\t\t\tacumulacion\t\t=> acumulacion,\n"
            + "\t\t\t\tdir2\t\t\t=> dir2,\n"
            + "\t\t\t\taddr_ant\t\t=> addr_ant);\n";

         for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
            code += "\n\tArith"
               + i
               + ": Arithmetic\n"
               + "\t\tport map(\tclk\t\t\t=> clk,\n"
               + "\t\t\t\tx\t\t\t=> in"
               + i
               + ",\n"
               + "\t\t\t\tpunto\t\t\t=> do(("
               + i
               + " * (N + P)) downto ("
               + (i - 1)
               + " * (N + P) + P + 1)),\n"
               + "\t\t\t\tpendiente\t\t=> do(("
               + i
               + " * (N + P) - N) "
               + "downto ("
               + (i - 1)
               + " * (N + P) + 1)),\n"
               + "\t\t\t\tpipeline\t\t=> pipeline,\n"
               + "\t\t\t\taddr_ant\t\t=> addr_ant,\n"
               + "\t\t\t\tgrado1\t\t=> grado"
               + i
               + "1,\n"
               + "\t\t\t\tgrado2\t\t=> grado"
               + i
               + "2,\n"
               + "\t\t\t\tnumfp\t\t\t=> numfp"
               + i
               + ");\n";
         }

         // Lo siguiente a escribir en el fichero depende de la memoria
         // donde se valla a implementar el sistema.
         if (XfvhdlProperties.memoryType.equals((String) "ROM")
            || XfvhdlProperties.memoryType.equals(
               (String) "Combinational Logic")) {
            code += "\n"
               + "\tMemCalc: ArithCalcMem\n"
               + "\t\tport map(\taddr\t\t\t=> addr_ant,\n"
               + "\t\t\t\tme\t\t\t=> me1,\n"
               + "\t\t\t\tdo\t\t\t=> do);\n";
         } else {
            code += "\n\tMemCalc: ArithCalc_RAM\n"
               + "\t\tport map(\tclk\t\t\t=> clk,\n"
               + "\t\t\t\twe\t\t\t=> me1,\n"
               + "\t\t\t\taddr\t\t\t=> addr_ant,\n"
               + "\t\t\t\tdi\t\t\t=> do,\n"
               + "\t\t\t\tdo\t\t\t=> do);\n";
         }

         for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
            code += "\n\tMF_grad"
               + i
               + ": MF_Grade\n"
               + "\t\tport map(\tgrado1\t\t=> grado"
               + i
               + "1,\n"
               + "\t\t\t\tgrado2\t\t=> grado"
               + i
               + "2,\n"
               + "\t\t\t\tnumfp\t\t\t=> numfp"
               + i
               + ",\n"
               + "\t\t\t\tentr_conta\t\t=> dir2("
               + i
               + "),\n"
               + "\t\t\t\tetiqueta\t\t=> addr_reg(((entradas - "
               + (i - 1)
               + ") * bits_etiq) downto \n\t\t\t\t\t\t\t"
               + "   (((entradas - "
               + i
               + ") * bits_etiq) + 1)),\n"
               + "\t\t\t\tgrado\t\t\t=> grados(("
               + i
               + " * grad) downto "
               + "\n\t\t\t\t\t\t\t   (("
               + (i - 1)
               + " * grad) + 1)));\n";
         }
      }

      // Lo siguiente a escribir en el fichero depende de la memoria
      // donde se valla a implementar el sistema.
      if (XfvhdlProperties.memoryType.equals((String) "ROM")
         || XfvhdlProperties.memoryType.equals(
            (String) "Combinational Logic")) {
         code += "\n"
            + "\tRulMem: RulesMem\n"
            + "\t\tport map(\taddr\t\t\t=> addr_reg,\n"
            + "\t\t\t\tme\t\t\t=> me2,\n"
            + "\t\t\t\tdo\t\t\t=> salida_mem_reg);\n"
            + "\n";
      } else {
         code += "\tRulesMem: RulesMem_RAM\n"
            + "\t\tport map(\tclk\t\t\t=> clk,\n"
            + "\t\t\t\twe\t\t\t=> me2,\n"
            + "\t\t\t\taddr\t\t\t=> addr_reg,\n"
            + "\t\t\t\tdi\t\t\t=> salida_mem_reg,\n"
            + "\t\t\t\tdo\t\t\t=> salida_mem_reg);\n";
      }

      // Aqui se introduce la operaci�n AND, que ser� el producto o
      // el m�nimo (por defecto)
      if (XfvhdlProperties.operationAnd.equalsIgnoreCase("prod")) {
         code += "\tProd: Product\n"
            + "\t\tport map(\tentrada\t\t=> grados,\n"
            + "\t\t\t\tsalida\t\t=> salida_min);\n";
      } else {
         code += "\tMin: Minimum\n"
            + "\t\tport map(\tentrada\t\t=> grados,\n"
            + "\t\t\t\tsalida\t\t=> salida_min);\n";
      }

      //A�ado a code el c�digo que depende del m�todo de 
      // defuzzificaci�n:
      String tmp = new String();
      tmp = defuzzification.generateOutputFlcComponent();
      code = code + tmp;

      code += "\n"
         + "\tDiv: Division\n"
         + "\t\tport map(\tdividendo\t\t=> dividendo,\n"
         + "\t\t\t\tdivisor\t\t=> divisor,\n"
         + "\t\t\t\tpipeline\t\t=> pipeline,\n"
         + "\t\t\t\tclk\t\t\t=> clk,\n"
         + "\t\t\t\tcociente\t\t=> output,\n"
         + "\t\t\t\tsali_valid\t\t=> valid_out);\n"
         + "\nend FPGA;";

      return code;
   }

   /**
   * M�todo que crea la cadena con las se�ales si estamos en calculo 
   * aritm�tico.
   */
   public String GenerateSignalWithCalcAritmetic() {
      String code = new String();

      code =
         "\tsignal clear_almacen\t: std_logic;\n"
            + "\tsignal pipeline\t\t: std_logic;\n"
            + "\tsignal me1\t\t\t: std_logic;\n"
            + "\tsignal me2\t\t\t: std_logic;\n"
            + "\tsignal addr_ant\t\t: "
            + "std_logic_vector(dir_ant downto 1);\n"
            + "\tsignal addr_reg\t\t: "
            + "std_logic_vector(dir_regl downto 1);\n"
            + "\tsignal acumulacion\t: std_logic;\n"
            + "\tsignal dir2\t\t\t: "
            + "std_logic_vector(entradas downto 1);\n"
            + "\tsignal do\t\t\t: std_logic_vector(M downto 1);\n";

      for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
         code += "\tsignal grado"
            + i
            + "1\t\t: std_logic_vector(grad downto 1);"
            + "\n"
            + "\tsignal grado"
            + i
            + "2\t\t: "
            + "std_logic_vector(grad downto 1);\n";
      }

      for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
         code += "\tsignal numfp"
            + i
            + "\t\t: std_logic_vector(bits_etiq "
            + "downto 1);\n";
      }

      code += "\tsignal grados\t\t: std_logic_vector((entradas * grad) "
         + "downto 1);\n"
         + "\tsignal etiquetas\t\t: "
         + "std_logic_vector((entradas * bits_etiq) "
         + "downto 1);\n"
         + "\tsignal salida_min\t\t: std_logic_vector(grad downto 1);\n"
         + "\tsignal salida_mem_reg\t: "
         + "std_logic_vector(w_reglas downto 1);\n"
         + "\tsignal dividendo\t\t: std_logic_vector(D downto 1);\n"
         + "\tsignal divisor\t\t: std_logic_vector(DR downto 1);\n";

      return code;
   }

   /**
   * M�todo que crea la cadena con las se�ales si NO estamos en calculo 
   * aritm�tico.
   */
   public String GenerateSignalWithoutCalcAritmetic() {
      String code = new String();

      code =
         "\tsignal clear_almacen\t: std_logic;\n"
            + "\tsignal pipeline\t\t: std_logic;\n"
            + "\tsignal me1\t\t\t: std_logic;\n"
            + "\tsignal me2\t\t\t: std_logic;\n"
            + "\tsignal addr_reg\t\t: "
            + "std_logic_vector(dir_regl downto 1);\n"
            + "\tsignal acumulacion\t: std_logic;\n"
            + "\tsignal dir2\t\t\t: "
            + "std_logic_vector(entradas downto 1);\n";

      for (int i = 1; i <= XfvhdlProperties.entradas; i++) {
         code += "\tsignal do"
            + i
            + "\t\t\t: std_logic_vector(M downto 1);\n";
      }

      code += "\tsignal grados\t\t: std_logic_vector((entradas * grad) "
         + "downto 1);\n"
         + "\tsignal etiquetas\t\t: "
         + "std_logic_vector((entradas * bits_etiq) "
         + "downto 1);\n"
         + "\tsignal salida_min\t\t: std_logic_vector(grad downto 1);\n"
         + "\tsignal salida_mem_reg\t: "
         + "std_logic_vector(w_reglas downto 1);\n"
         + "\tsignal dividendo\t\t: std_logic_vector(D downto 1);\n"
         + "\tsignal divisor\t\t: std_logic_vector(DR downto 1);\n";

      return code;
   }

}
